package com.mxf.code.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.mxf.code.service.ChildrenTree;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 对 Stream 流操作的封装
 */
public class StreamUtil {

    private StreamUtil() {
    }

    /**
     * 转换为Map
     *
     * @param list     对象list
     * @param function 消费型函数
     * @param <S>      key
     * @param <T>      value, 对象
     * @return 转为map
     */
    public static <S, T> Map<S, T> listToMap(List<T> list, Function<T, S> function) {
        if (CollUtil.isEmpty(list) || Objects.isNull(function)) {
            return new HashMap<>(0);
        }
        return list.stream().collect(Collectors.toMap(function, Function.identity(), (o, n) -> n));
    }

    /**
     * 转换为ListMap
     *
     * @param list     对象list
     * @param function 消费型函数
     * @param <S>      key
     * @param <T>      对象
     * @return 对象List的一个map
     */
    public static <S, T> Map<S, List<T>> listToListMap(List<T> list, Function<T, S> function) {
        if (CollUtil.isEmpty(list) || Objects.isNull(function)) {
            return new HashMap<>(0);
        }
        return list.stream().collect(Collectors.toMap(function, CollUtil::newArrayList, (o, n) -> {
            o.addAll(n);
            return o;
        }));
    }

    /**
     * 转换为Map
     *
     * @param list          对象list
     * @param keyFunction   key消费型函数
     * @param valueFunction value消费型函数
     * @param <T>           对象
     * @param <K>           key
     * @param <V>           value
     * @return 转换为map
     */
    public static <T, K, V> Map<K, V> listToMap(List<T> list, Function<T, K> keyFunction, Function<T, V> valueFunction) {
        if (CollUtil.isEmpty(list) || Objects.isNull(keyFunction) || Objects.isNull(valueFunction)) {
            return new HashMap<>(0);
        }
        return list.stream().collect(Collectors.toMap(keyFunction, valueFunction, (o, n) -> n));
    }

    /**
     * list转为另外一个list
     *
     * @param list            对象list
     * @param convertFunction 转换消费型函数
     * @param <T>             对象
     * @param <R>             转换后的结果对象
     * @return 另外一个list
     */
    public static <T, R> List<R> listToList(List<T> list, Function<T, R> convertFunction) {
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>(0);
        }
        return list.stream().map(convertFunction).collect(Collectors.toList());
    }

    /**
     * 将list转为tree
     *
     * @param list           将要转为树形结构的集合
     * @param predicate      顶层，第一层的判断 例如 (t -> t.getId()==0)
     * @param childPredicate 父子集关联的判断，例如 (parent, child) -> Objects.equals(parent.getId(), child.getParentId())
     * @param <T>            对象，需要实现{@link ChildrenTree}
     * @return 树集合
     */
    public static <T extends ChildrenTree<T>> List<T> listToTree(List<T> list, Predicate<T> predicate, BiPredicate<T, T> childPredicate) {
        return list.stream()
                .filter(predicate)
                .map(t -> {
                    t.setChildren(getChildren(list, t, childPredicate));
                    return t;
                }).collect(Collectors.toList());
    }

    private static <T extends ChildrenTree<T>> List<T> getChildren(List<T> list, T parent, BiPredicate<T, T> childPredicate) {
        return list.stream()
                .filter(t -> childPredicate.test(parent, t))
                .map(t -> {
                    t.setChildren(getChildren(list, t, childPredicate));
                    return t;
                })
                .collect(Collectors.toList());
    }

    /**
     * 查找tree，从上往下进行拼接
     *
     * @param tree      树
     * @param predicate 判断查找的那个对象
     * @param function  转换为对象拼接的名称
     * @param separator 分隔符
     * @param <T>       对象
     * @return 查找tree，从上往下进行拼接 字符串
     */
    public static <T extends ChildrenTree<T>> String findInTree(List<T> tree, Predicate<T> predicate, Function<T, String> function, String separator) {
        String result;
        for (T t : tree) {
            // 先判断自身
            if (predicate.test(t)) {
                return function.apply(t);
            }
            if (CollUtil.isNotEmpty(t.getChildren())) {
                result = findInTree(t.getChildren(), predicate, function, separator);
                if (Objects.nonNull(result)) {
                    return function.apply(t) + separator + result;
                }
            }
        }
        return "";
    }

    /**
     * 并集
     *
     * @param list    对象list
     * @param function 转换
     * @param <T>      对象
     * @param <R>      转换后的对象
     * @return 结果对象集合
     */
    public static <T, R> List<R> concat(Function<T, R> function, List<T>... list) {
        Stream<T>[] array = Arrays.stream(list).map(Collection::stream).toArray(Stream[]::new);
        return Stream.of(array)
                .flatMap(Function.identity())
                .map(function)
                .collect(Collectors.toList());
    }

    /**
     * 获取两个list的交集
     *
     * @param list1     第一个对象list
     * @param list2     第二个对象list
     * @param predicate 判断两个list是否相等
     * @param function  转换输出的结果
     * @param <T1>      第一个对象
     * @param <T2>      第二个对象
     * @param <R>       结果对象
     * @return 交集
     */
    public static <T1, T2, R> List<R> getListIntersection(List<T1> list1, List<T2> list2, BiPredicate<T1, T2> predicate, Function<T1, R> function) {
        return list1.stream().map(a -> list2.stream().map(b -> {
                    if (predicate.test(a, b)) {
                        return function.apply(a);
                    }
                    return null;
                }).filter(Objects::nonNull).findFirst()
        ).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList());
    }

    /**
     * 获取两个list的差集，简单的来说就是 list1 - list2 = list3
     *
     * @param list1     第一个对象list
     * @param list2     第二个对象list
     * @param predicate 判断两个list是否相等，相等的才会减去
     * @param function  转换输出的结果
     * @param <T1>      第一个对象
     * @param <T2>      第二个对象
     * @param <R>       结果对象
     * @return 差集
     */
    public static <T1, T2, R> List<R> getListOfDifference(List<T1> list1, List<T2> list2, BiPredicate<T1, T2> predicate, Function<T1, R> function) {
        return list1.stream()
                .filter(a -> list2.stream().noneMatch(b -> predicate.test(a, b)))
                .map(function)
                .collect(Collectors.toList());
    }

    /**
     * list转为字符串
     *
     * @param list     对象list
     * @param function 转换字符串的消费型函数
     * @param <T>      对象
     * @param <R>      转换对象
     * @return 字符串
     */
    public static <T, R> String listToString(List<T> list, Function<T, R> function) {
        return listToString(list, function, ",");
    }

    /**
     * list转为字符串
     *
     * @param list      对象list
     * @param function  转换字符串的消费型函数
     * @param separator 间隔符
     * @param <T>       对象
     * @param <R>       转换对象
     * @return 字符串
     */
    public static <T, R> String listToString(List<T> list, Function<T, R> function, String separator) {
        return list.stream().map(function).map(Convert::toStr).distinct().collect(Collectors.joining(separator));
    }

    /**
     * 在list查询符合条件的对象
     *
     * @param list      对象list
     * @param predicate 判断型函数
     * @param function  转换消费型函数
     * @param <T>       对象
     * @param <R>       转换后的对象
     * @return 转换后的对象
     */
    public static <T, R> R findInList(List<T> list, Predicate<T> predicate, Function<T, R> function) {
        return findInList(list, predicate, function, null);
    }

    /**
     * 在list查询符合条件的对象
     *
     * @param list         对象list
     * @param predicate    判断型函数
     * @param function     转换消费型函数
     * @param defaultValue 默认值
     * @param <T>          对象
     * @param <R>          转换后的对象
     * @return 查找到后转换的对象
     */
    public static <T, R> R findInList(List<T> list, Predicate<T> predicate, Function<T, R> function, R defaultValue) {
        return list.stream().filter(predicate).findFirst().map(function).orElse(defaultValue);
    }

    /**
     * 在树形结构中查询，指定的对象，并将其转换为结果对象
     *
     * @param tree      树形结构
     * @param current   需要查找匹配的
     * @param predicate 匹配的条件
     * @param function  转换的函数
     * @param <T>       树形接口的对象
     * @param <R>       转换后的对象
     * @return 转换后的对象集合
     */
    public static <T extends ChildrenTree<T>, R> List<R> findInTree(List<T> tree, T current, BiPredicate<T, T> predicate, Function<T, R> function) {
        return findInTree(new ArrayList<>(), tree, current, predicate, function, false);
    }

    private static <T extends ChildrenTree<T>, R> List<R> findInTree(List<R> result, List<T> tree, T current, BiPredicate<T, T> predicate, Function<T, R> function, boolean find) {
        boolean f = false;
        for (T t : tree) {
            // 如果已经找到了
            if (f) {
                continue;
            }
            f = predicate.test(t, current);
            boolean newFind = find || f;
            if (newFind) {
                result.add(function.apply(t));
            }
            List<T> children = t.getChildren();
            if (CollUtil.isNotEmpty(children)) {
                findInTree(result, children, current, predicate, function, newFind);
            }
        }
        return result;
    }


}
